package com.joker.aigc.chat.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.joker.aigc.chat.domain.entity.Contact;
import com.joker.aigc.chat.domain.entity.Room;
import com.joker.aigc.chat.domain.entity.RoomFriend;
import com.joker.aigc.chat.domain.enums.HotFlagEnum;
import com.joker.aigc.chat.domain.enums.RoomTypeEnum;
import com.joker.aigc.chat.mapper.ContactMapper;
import com.joker.aigc.chat.mapper.RoomFriendMapper;
import com.joker.aigc.chat.mapper.RoomMapper;
import com.joker.aigc.chat.service.UserFriendService;
import com.joker.aigc.chat.service.event.UserApplyEvent;
import com.joker.aigc.domain.dto.UserApplyCriteria;
import com.joker.aigc.domain.dto.UserApplyDto;
import com.joker.aigc.domain.dto.UserFriendDto;
import com.joker.aigc.domain.entity.User;
import com.joker.aigc.domain.entity.UserApply;
import com.joker.aigc.domain.entity.UserFriend;
import com.joker.aigc.domain.enums.AppHttpCodeEnum;
import com.joker.aigc.domain.enums.UserApplyStatusEnum;
import com.joker.aigc.domain.enums.UserFriendStatusEnum;
import com.joker.aigc.domain.mapstruct.UserApplyMapstruct;
import com.joker.aigc.domain.mapstruct.UserFriendMapstruct;
import com.joker.aigc.domain.mapstruct.UserMapstruct;
import com.joker.aigc.domain.vo.UserApplyVo;
import com.joker.aigc.domain.vo.UserVo;
import com.joker.aigc.exception.BusinessException;
import com.joker.aigc.mapper.UserApplyMapper;
import com.joker.aigc.mapper.UserFriendMapper;
import com.joker.aigc.mapper.UserMapper;
import com.joker.aigc.utils.AssertUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;

/**
 * @author 20346
 *  针对表【user_friend(用户联系人表)】的数据库操作Service实现
 * @since 2023-12-17 11:26:08
 */
@Slf4j
@RequiredArgsConstructor
@Service
@Transactional
public class UserFriendServiceImpl extends ServiceImpl<UserFriendMapper, UserFriend> implements UserFriendService {


    final UserApplyMapper userApplyMapper;
    final RoomFriendMapper roomFriendMapper;
    final RoomMapper roomMapper;
    final UserMapper userMapper;
    final ContactMapper contactMapper;
    final UserApplyMapstruct userApplyMapstruct;
    final UserMapstruct userMapstruct;
    final UserFriendMapstruct userFriendMapstruct;
    final ApplicationEventPublisher applicationEventPublisher;


    /**
     * @param applyUid 申请人id
     * @param dto      申请信息
     */
    @Override
    public void addUserApply(Long applyUid, UserApplyDto dto) {

        // 第一步  校验 朋友的id是否存在
        UserApply userApply = userApplyMapstruct.toEntity(dto);
        log.info("applyFriend-->{}", userApply);
        userApply.setApplyUid(applyUid);
        userApply.setStatus(UserApplyStatusEnum.APPLY.getId());

        User friend = userMapper.selectById(dto.getToUid());
        AssertUtil.isNotEmpty(friend, "朋友id是否存在");

        User apply = userMapper.selectById(applyUid);
        AssertUtil.isNotEmpty(apply, "申请人不存在");

        // 第二步 检查2个人是否已经是好友了
        UserFriend userFriend = getOne(
                new LambdaQueryWrapper<UserFriend>().eq(UserFriend::getUid, applyUid)
                        .eq(UserFriend::getFriendUid, userApply.getToUid())
        );
        // 如果2个人之间已经是好友了
        if (userFriend != null) {
            throw new BusinessException(AppHttpCodeEnum.USER_FRIEND_EXIST);
        }

        //第三步 校验该申请是否已经存在，则不能进行重复申请
        UserApply alreadExist = userApplyMapper.selectOne(new LambdaQueryWrapper<UserApply>()
                .eq(UserApply::getApplyUid, userApply.getApplyUid())
                .eq(UserApply::getToUid, userApply.getToUid()));

        if (alreadExist != null) {
            throw new BusinessException(AppHttpCodeEnum.USER_APPLY_EXIST);
        }

        // 第四步，发布事件进行监听
        applicationEventPublisher.publishEvent(new UserApplyEvent(this, userApply));

    }


    /**
     * 通过 好友申请。两个人开始正式的聊天
     *
     * @param id userapply 表的id.
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void accessUserApply(Long id) {

        // 第一步，查找申请信息是否存在
        UserApply userApply = userApplyMapper.selectById(id);
        //如果申请不存在
        AssertUtil.isNotEmpty(userApply, "该申请不存在");
        //如果存在，则设置申请信息为通过状态
        userApply.setStatus(UserApplyStatusEnum.FRIEND.getId());
        //变更数据库
        userApplyMapper.updateById(userApply);

        // 第二步,开始添加好友表。保存2条记录。2条记录的原因。是为了后续做拉黑操作。单方面拉黑
        UserFriend u1 = new UserFriend();
        u1.setUid(userApply.getApplyUid());
        u1.setFriendUid(userApply.getToUid());
        u1.setStatus(UserFriendStatusEnum.FRIEND.getId());

        save(u1);
        // 保存
        UserFriend u2 = new UserFriend();
        u2.setUid(userApply.getToUid());
        u2.setFriendUid(userApply.getApplyUid());
        u2.setStatus(UserFriendStatusEnum.FRIEND.getId());

        save(u2);

        // 第三步，就是创建聊天的单聊房间
        Room room = new Room();
        RoomFriend roomFriend = new RoomFriend();
        // 设置单聊
        room.setType(RoomTypeEnum.FRIEND.getId());
        // 并不是全局聊天室
        room.setHotFlag(HotFlagEnum.NOT.getId());
        room.setActiveTime(new Date()); // 设置激活时间的表
        roomMapper.insert(room);

        log.info("插入后的romm:{}", room);
        // 第四步，创建 具体单聊房间的信息
        roomFriend.setRoomId(room.getId());
        roomFriend.setStatus(0);
        // uid1 确保id是比较小的那个
        Long uid1 = userApply.getApplyUid();
        Long uid2 = userApply.getToUid();
        // 如果现在 uid1比较大。则和uid2进行交换
        if (uid1 > uid2) {
            Long tmp = uid1;
            uid1 = uid2;
            uid2 = tmp;
        }
        roomFriend.setUid1(uid1);
        roomFriend.setUid2(uid2);
        roomFriend.setRoomKey(uid1 + "_" + uid2);
        roomFriendMapper.insert(roomFriend);

        // 第五步，创建会话 会话是创建2个 用来记录用户什么时间读取了什么信息
        Contact contact1 = new Contact();
        contact1.setActiveTime(new Date());
        contact1.setUid(userApply.getApplyUid());
        contact1.setReadTime(new Date());
        contact1.setRoomId(room.getId());

        contactMapper.insert(contact1);

        Contact contact2 = new Contact();
        contact2.setActiveTime(new Date());
        contact2.setUid(userApply.getToUid());
        contact2.setReadTime(new Date());
        contact2.setRoomId(room.getId());
        contactMapper.insert(contact2);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteUserApply(Long id) {
        // 添加朋友
        UserApply userApply = userApplyMapper.selectById(id);
        // 如果好友申请不存在
        AssertUtil.isNotEmpty(userApply, "该好友申请不存在");
        // 通过
        userApply.setStatus(UserApplyStatusEnum.BLANK.getId());
        // 拒绝好友
        userApplyMapper.updateById(userApply);
    }


    @Override
    public List<UserVo> queryUserFriend(Long uid) {
        MPJLambdaWrapper<UserFriend> wrapper = new MPJLambdaWrapper<>();
        wrapper.selectAll(User.class)
                .leftJoin(User.class, User::getId, UserFriend::getFriendUid)
                .eq(UserFriend::getUid, uid)
                .eq(UserFriend::getStatus, UserFriendStatusEnum.FRIEND.getId());

        List<User> userList = getBaseMapper().selectJoinList(User.class, wrapper);

        return userMapstruct.toVO(userList);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void blackFriend(UserFriendDto userFriendDto) {
        UserFriend userFriend = getBaseMapper().selectOne(new LambdaQueryWrapper<UserFriend>()
                .eq(UserFriend::getUid, userFriendDto.getUid())
                .eq(UserFriend::getFriendUid, userFriendDto.getFriendUid()));

        AssertUtil.isNotEmpty(userFriend, "对不起，好友列表查找不到该申请");
        userFriend.setStatus(UserFriendStatusEnum.BLANK.getId());

        saveOrUpdate(userFriend); // 更新状态

    }

    @Override
    public Page<UserApplyVo> queryUserApplyInfo(Long uid, UserApplyCriteria criteria) {

        MPJLambdaWrapper<UserApply> wrapper = new MPJLambdaWrapper<>();

        wrapper.selectAll(UserApply.class)
                .select(User::getUsername, User::getAvatar)
                .selectAs(User::getId, "uid")
                .leftJoin(User.class, User::getId, UserApply::getToUid)
                .eq(UserApply::getToUid, uid);

        return userApplyMapper.selectJoinPage(
                new Page<UserApplyVo>(criteria.getPageNum(), criteria.getPageSize()),
                UserApplyVo.class,
                wrapper
        );
    }

}




